#include "./renderUtils.h"

#include "../layer/layer.hpp"

#include <gtest/gtest.h>
#include <spdlog/spdlog.h>
#include <vsg/all.h>
#include <vsgQt/Window.h>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <entt/entt.hpp>

#include <iostream>

// struct Vec2 {
//   double x;
//   double y;
// }

struct Mesh {
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;  // 顶点索引

  Mesh() {
    vertices = vsg::vec2Array::create();
    indices = vsg::uintArray::create();
  };

  ~Mesh(){};

  Mesh(uint32_t v_size, uint32_t i_size) {
    vertices = vsg::vec2Array::create(v_size);
    indices = vsg::uintArray::create(i_size);
  };
};

struct FillUniform {
  vsg::vec4 color;
  int type;
  float width;
  float space;
  int use_overlapping_discoloration;
};

struct FillShaderMaterial {
  vsg::ref_ptr<vsg::Value<FillUniform>> uniform;
  vsg::Path vertexShader;
  vsg::Path fragmentShader;
  FillShaderMaterial() {}
  ~FillShaderMaterial() {}

  FillShaderMaterial(const FillUniform& u, const vsg::Path& v_path, const vsg::Path& f_path) {
    uniform = vsg::Value<FillUniform>::create();
    uniform->properties.dataVariance = vsg::DYNAMIC_DATA;
    set_uniform(u);

    vertexShader = v_path;
    fragmentShader = f_path;
  }

  void set_uniform(const FillUniform& u) {
    uniform->value().color = u.color;
    uniform->value().type = u.type;
    uniform->value().width = u.width;
    uniform->value().space = u.space;
    uniform->value().use_overlapping_discoloration = u.use_overlapping_discoloration;
    uniform->dirty();
  }
};

class VsgWindow : public vsgQt::Window {
  // Q_OBJECT
 public:
  VsgWindow(vsg::ref_ptr<vsgQt::Viewer> in_viewer, vsg::ref_ptr<vsg::WindowTraits> in_traits, QWindow* parent){};
  VsgWindow(){};
  ~VsgWindow(){};

  vsg::ref_ptr<vsg::Camera> camera;
  QWidget* map_widget;

 protected:
  void keyPressEvent(QKeyEvent* e) override { vsgQt::Window::keyPressEvent(e); };
  void keyReleaseEvent(QKeyEvent* e) override{};
  void mouseMoveEvent(QMouseEvent* e) override{};
  void mousePressEvent(QMouseEvent* e) override{};
  void mouseReleaseEvent(QMouseEvent* e) override{};
  void resizeEvent(QResizeEvent* e) override{};
  void wheelEvent(QWheelEvent* e) override{};
};

std::tuple<vsgQt::Window*, vsg::ref_ptr<vsg::Camera>> createWindow(vsg::ref_ptr<vsgQt::Viewer> viewer, vsg::ref_ptr<vsg::WindowTraits> traits,
                                                                   vsg::ref_ptr<vsg::Group> vsg_scene, QWindow* parent, const QString& title = {}) {
  auto window = new vsgQt::Window(viewer, traits, parent);
  window->setTitle(title);
  window->initializeWindow();

  // if this is the first window to be created, use its device for future window creation.
  if (!traits->device)
    traits->device = window->windowAdapter->getOrCreateDevice();

  auto width_half = static_cast<double>(traits->width / 2);
  auto height_half = static_cast<double>(traits->height / 2);
  auto ortho = vsg::Orthographic::create(-width_half, width_half, -height_half, height_half, 0.0f, 10.0f);
  auto lookAt = vsg::LookAt::create(vsg::dvec3(0.0, 0.0, 5.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 1.0, 0.0));
  auto viewport = vsg::ViewportState::create(0, 0, traits->width, traits->height);
  auto camera = vsg::Camera::create(ortho, lookAt, viewport);
  auto commandGraph = vsg::createCommandGraphForView(*window, camera, vsg_scene);
  viewer->addRecordAndSubmitTaskAndPresentation({commandGraph});
  return std::make_tuple(window, camera);
}

template <typename UTy>
static void genVsgPipe(vsg::StateGroup* group, vsg::ref_ptr<vsg::Options> options, vsg::Path vertexShaderPath, vsg::Path fragmentShaderPath,
                       UTy uniform, VkPrimitiveTopology primitiveTopology, bool instance) {

  auto vertexShader = vsg::ShaderStage::read(VK_SHADER_STAGE_VERTEX_BIT, "main", vsg::findFile(vertexShaderPath, options));
  auto fragmentShader = vsg::ShaderStage::read(VK_SHADER_STAGE_FRAGMENT_BIT, "main", vsg::findFile(fragmentShaderPath, options));
  if (!vertexShader || !fragmentShader) {
    SPDLOG_INFO("Could not create shaders.");
    return;
  }

  // Uniform绑定
  vsg::DescriptorSetLayoutBindings descriptorBindings{
      {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT, nullptr},  // ClipSettings uniform
  };

  auto descriptorSetLayout = vsg::DescriptorSetLayout::create(descriptorBindings);

  // projection, view, and model matrices, actual push constant calls automatically provided by the VSG's RecordTraversal
  vsg::PushConstantRanges pushConstantRanges{{VK_SHADER_STAGE_VERTEX_BIT, 0, 128}};

  vsg::VertexInputState::Bindings vertexBindingsDescriptions = vsg::VertexInputState::Bindings{
      VkVertexInputBindingDescription{0, sizeof(vsg::vec2), VK_VERTEX_INPUT_RATE_VERTEX},  // vertex data
  };
  vsg::VertexInputState::Attributes vertexAttributeDescriptions = vsg::VertexInputState::Attributes{
      VkVertexInputAttributeDescription{0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0},  // vertex data
  };

  vsg::GraphicsPipelineStates pipelineStates{vsg::VertexInputState::create(vertexBindingsDescriptions, vertexAttributeDescriptions),
                                             vsg::InputAssemblyState::create(),
                                             vsg::RasterizationState::create(),
                                             vsg::MultisampleState::create(),
                                             vsg::ColorBlendState::create(),
                                             vsg::DepthStencilState::create()};

  auto pipelineLayout = vsg::PipelineLayout::create(vsg::DescriptorSetLayouts{descriptorSetLayout}, pushConstantRanges);
  auto graphicsPipeline = vsg::GraphicsPipeline::create(pipelineLayout, vsg::ShaderStages{vertexShader, fragmentShader}, pipelineStates);
  auto bindGraphicsPipeline = vsg::BindGraphicsPipeline::create(graphicsPipeline);

  // create UniformValue and associated DescriptorSets and binding
  auto uniformBuffer = vsg::DescriptorBuffer::create(uniform, 0);
  auto descriptorSet = vsg::DescriptorSet::create(descriptorSetLayout, vsg::Descriptors{uniformBuffer});
  auto bindDescriptorSet = vsg::BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, descriptorSet);

  group->add(bindGraphicsPipeline);
  group->add(bindDescriptorSet);
};

class MeshNode : public vsg::Inherit<vsg::StateGroup, MeshNode> {

 public:
  Mesh _mesh;
  std::string name;

 public:
  MeshNode(){};
  ~MeshNode(){};

  // template <typename... Args>
  // static vsg::ref_ptr<MeshNode> create(Args&&... args) {
  //   return vsg::ref_ptr<MeshNode>(new MeshNode(std::forward<Args>(args)...));
  // }

  virtual void addMesh(const Mesh& mh, bool fill = false) {
    auto drawCommands = vsg::Commands::create();
    drawCommands->addChild(vsg::BindVertexBuffers::create(0, vsg::DataList{mh.vertices}));
    drawCommands->addChild(vsg::BindIndexBuffer::create(mh.indices));
    drawCommands->addChild(vsg::DrawIndexed::create(mh.indices->size(), 1, 0, 0, 0));
    this->addChild(drawCommands);
  }
};

class GeoMeshNode : public vsg::Inherit<MeshNode, GeoMeshNode> {

 public:
  GeoMeshNode(){};
  ~GeoMeshNode(){};

  template <typename T>
  void createPipe(vsg::ref_ptr<vsg::Options> options, T material, VkPrimitiveTopology primitiveTopology) {
    genVsgPipe(this, options, material.vertexShader, material.fragmentShader, material.uniform, primitiveTopology, false);
  }

  template <typename... Args>
  static vsg::ref_ptr<GeoMeshNode> create(Args&&... args) {
    return vsg::ref_ptr<GeoMeshNode>(new GeoMeshNode(std::forward<Args>(args)...));
  }
};

namespace qbe {

using FillUniformV = vsg::Value<FillUniform>;

layer::RenderParam getLayerParam(std::vector<std::string> layer_info) {
  std::string fill_color = layer_info[0];
  std::string line_color = layer_info[1];
  std::string fill_stype = layer_info[2];
  std::string line_stype = layer_info[3];

  auto colorToRGB = [](const std::string& fill_color) {
    QColor qc(fill_color.c_str());
    int r, g, b;
    qc.getRgb(&r, &g, &b);
    return vsg::vec4(r / 255.0, g / 255.0, b / 255.0, 1.0);
  };

  layer::RenderParam res;
  res.fill_color = colorToRGB(fill_color);
  res.border_color = colorToRGB(line_color);
  res.fill_param = layer::FILL_TYPES_TABLE[fill_stype];
  res.border_param = layer::BORDER_TYPES_TABLE[line_stype];

  return res;
}

void buildFillNode(vsg::ref_ptr<vsg::Options> options, vsg::Paths searchPaths, vsg::Path vertexShaderPath, vsg::Path fragmentShaderPath,
                   vsg::ref_ptr<vsg::StateGroup>& res1, vsg::ref_ptr<FillUniformV>& res2) {
  vsg::ref_ptr<vsg::ShaderStage> vertexShader =
      vsg::ShaderStage::read(VK_SHADER_STAGE_VERTEX_BIT, "main", vsg::findFile(vertexShaderPath, searchPaths));
  vsg::ref_ptr<vsg::ShaderStage> fragmentShader =
      vsg::ShaderStage::read(VK_SHADER_STAGE_FRAGMENT_BIT, "main", vsg::findFile(fragmentShaderPath, searchPaths));
  if (!vertexShader || !fragmentShader) {
    std::cout << "Could not create shaders." << std::endl;
    assert(0);
  }

  vsg::DescriptorSetLayoutBindings descriptorBindings{
      {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}};

  auto descriptorSetLayout = vsg::DescriptorSetLayout::create(descriptorBindings);

  vsg::PushConstantRanges pushConstantRanges{
      {VK_SHADER_STAGE_VERTEX_BIT, 0,
       128}  // projection, view, and model matrices, actual push constant calls automatically provided by the VSG's RecordTraversal
  };

  vsg::VertexInputState::Bindings vertexBindingsDescriptions{VkVertexInputBindingDescription{0, sizeof(vsg::vec3), VK_VERTEX_INPUT_RATE_VERTEX}};

  vsg::VertexInputState::Attributes vertexAttributeDescriptions{VkVertexInputAttributeDescription{0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0}};

  vsg::GraphicsPipelineStates pipelineStates{vsg::VertexInputState::create(vertexBindingsDescriptions, vertexAttributeDescriptions),
                                             vsg::InputAssemblyState::create(),
                                             vsg::RasterizationState::create(),
                                             vsg::MultisampleState::create(),
                                             vsg::ColorBlendState::create(),
                                             vsg::DepthStencilState::create()};

  auto pipelineLayout = vsg::PipelineLayout::create(vsg::DescriptorSetLayouts{descriptorSetLayout}, pushConstantRanges);
  auto graphicsPipeline = vsg::GraphicsPipeline::create(pipelineLayout, vsg::ShaderStages{vertexShader, fragmentShader}, pipelineStates);
  auto bindGraphicsPipeline = vsg::BindGraphicsPipeline::create(graphicsPipeline);

  vsg::ref_ptr<FillUniformV> uniform = FillUniformV::create();
  res2 = uniform;

  auto uniformDesc = vsg::DescriptorBuffer::create(uniform, 0, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);

  auto descriptorSet = vsg::DescriptorSet::create(descriptorSetLayout, vsg::Descriptors{uniformDesc});
  auto bindDescriptorSet = vsg::BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, descriptorSet);

  auto scenegraph = vsg::StateGroup::create();
  scenegraph->add(bindGraphicsPipeline);
  scenegraph->add(bindDescriptorSet);

  res1 = scenegraph;
}

vsg::ref_ptr<vsg::Group> getLayerNode(std::function<std::vector<std::string>(uint32_t)>& _funvc, uint32_t layer, qlib::Rectangle::s_ptr rectangle) {
  auto getLayerStyleInfoOnNumFunc = _funvc;
  auto layer_info_str = getLayerStyleInfoOnNumFunc(layer);

  if (layer_info_str.empty()) {
    assert(0);
  }

  layer::RenderParam param = getLayerParam(layer_info_str);

  auto res = vsg::Group::create();

  auto options = vsg::Options::create();
  options->fileCache = vsg::getEnv("VSG_FILE_CACHE");
  options->paths = {
      "../../../data",
      "./data",
  };

  vsg::Paths searchPaths;
  searchPaths.push_back("./");
  searchPaths.push_back("../../../data");
  searchPaths.push_back("./data");

  {
    FillUniform fill_uniform;
    fill_uniform.color = param.fill_color;
    fill_uniform.type = param.fill_param.type;
    fill_uniform.width = param.fill_param.width;
    fill_uniform.space = param.fill_param.space;
    fill_uniform.use_overlapping_discoloration = 1;

    FillShaderMaterial material(fill_uniform, "shaders/cell/layout-poly.vert.spv", "shaders/cell/layout-poly.frag.spv");
    auto poly_fill_node = GeoMeshNode::create();
    poly_fill_node->createPipe(options, material, VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
    res->addChild(poly_fill_node);

    auto points = rectangle->to_polygon()->points;

    Mesh mesh(4, 6);
    mesh.vertices->at(0) = vsg::vec2(points[0].x, points[0].y);
    mesh.vertices->at(1) = vsg::vec2(points[1].x, points[1].y);
    mesh.vertices->at(2) = vsg::vec2(points[2].x, points[2].y);
    mesh.vertices->at(3) = vsg::vec2(points[3].x, points[3].y);

    mesh.indices->at(0) = 2;
    mesh.indices->at(1) = 1;
    mesh.indices->at(2) = 0;
    mesh.indices->at(3) = 3;
    mesh.indices->at(4) = 2;
    mesh.indices->at(5) = 0;
    poly_fill_node->addMesh(mesh);
  }

  return res;
}

}  // namespace qbe